from __future__ import division

from plyny.table import *

import unittest


#DataTable = SqlDataTable


def mapper(x):
    return (x[0] + 1, x[1] + 1)


class TestCase(unittest.TestCase):
    def x_test_stream_join(self):
        a = DataTable('a', 'c')
        b = DataTable('a', 'd')

        a.add(0, 0)
        a.add(1, 1)
        a.add(3, 3)

        b.add(0, 10)
        b.add(2, 20)
        b.add(3, 30)

        x = DataTable('a', 'c', 'd')
        x.add(0, 0, 10)
        x.add(1, 1, None)
        x.add(2, None, 20)
        x.add(3, 3, 30)
        v = DataTable.sorted_outer_join('a', a, b)
        self.assertEquals(v, x)
        v = DataTable.sorted_join('a', a, b)
        self.assertEquals(v.list_of_lists(), [[0, 0, 10], [3, 3, 30]])

        c = DataTable('a', 'e', 'f')
        c.add(9, 9, 9)

        self.assertEquals(DataTable.sorted_outer_join('a', a, b,
            c).list_of_lists(), [[0, 0, 10, None, None], [1, 1, None, None,
                None], [2, None, 20, None, None], [3, 3, 30, None, None], [9,
                    None, None, 9, 9]])

#        a.add(
        self.assertEquals(DataTable.left_softed_join('a', a, b, c).list_of_lists(), [[0, 0, 10, None, None], [1, 1, None, None, None], [3, 3, 30, None, None]])
        a.add(9, 19)
        a.add(19, 99)
        self.assertEquals(DataTable.left_softed_join('a', a, b,
            c).list_of_lists(), [[0, 0, 10, None, None], [1, 1, None, None,
                None], [3, 3, 30, None, None], [9, 19, None, 9, 9], [19, 99,
                    None, None, None]])

    def x_test_cdf(self):
        d = DataTable('a', 'c')
        d.add(1, 1)
        d.add(0, 2)
        d.add(0, 3)
        d.add(0, 4)
        d.add(0, 5)
        d.add(0, 6)
        p = d.cdf('c')
        p.save('mmm')

    def test_pmap(self):
        d = DataTable('c', 'd').extend([(x, x + 1) for x in xrange(24)])

        dp = d.parallel_map(mapper, 10)
        self.assertEquals(d.range(1, None), dp.range(len(dp) - 1))

        dp = d.pmap(mapper, 10)
        self.assertEquals(d.range(1, None), dp.range(len(dp) - 1))

        dp = d.pmap(lambda x: x, 10, False)
        self.assertEquals(d, dp)

        dp = d.pmap(mapper, 10, False)
        self.assertEquals(d.range(1, None), dp.range(len(dp) - 1))

    def test_type_guess(self):
        d = DataTable()
        d.add(1, 2.33337, 3)
        self.assertEquals(d._data.types(), [int, float, int])
        #print d.table()

    def test_enumerate(self):
        d = self.make().enumerate()
        self.assertEquals(list(d.columns('n')), range(2))

        d = self.make()
        d.add(4, 5, 6)
        d.add(1, 5, 6)
        d.add(3, 5, 6)
        d = d.rank('a')
        self.assertEquals(list(d.columns('n')), [0, 0, 1, 2, 2])

    def x_test_rank(self):
        d = DataTable()
        d.add(1)
        d.add(2)
        d.add(1)
        d.add(3)
        d.add(4)
        d.add(1)
        d.add(5)

        x = d.rank('a')
        self.assertEquals(x.list_of_lists(),[[1, 0], [2, 1], [1, 0], [3, 2], [4, 3], [1, 0], [5, 4]])

    def test_no_column_names(self):
        d = DataTable()
        self.assertRaises(ValueError, lambda x: getattr(x, 'column_names'), d)
        d.add(1, 2, 3)
        self.assertEquals(d.column_names, tuple('abc'))
#        self.assertRaises(ValueError, d.add, 1, 2, 3, 4)

    def test_get_group(self):
        a = DataTable('a', 'b')
        a.add(0, 1)
        a.add(2, 3)

        self.assertEquals(a.get_group(0, 1).list_of_lists(), a.list_of_lists())
        self.assertRaises(ValueError, a.get_group, 0, 3)
        a.add(4, 5)
        a.add(6, 7)
        a.add(8, 9)
        a.add(10, 11)
        self.assertEquals(a.get_group(3, 4).list_of_lists(), [[6, 7]])

    def test_streamstream(self):
        d1 = DataTable('c', 'd').extend([(x, x + 1) for x in xrange(10)])
        d2 = DataTable('c', 'd').extend([(x, x + 1) for x in xrange(10, 20)])
        d3 = DataTable('c', 'd').extend([(x, x + 1) for x in xrange(20, 30)])

        s = StreamStreamTable(d1, d2, d3)
        self.assertEquals(s.column_names, ('c', 'd'))

        sl = s.slice(4, 9)
        self.assertEquals(len(sl), 5)
        self.assertEquals(sl.list_of_lists(), [[4, 5], [5, 6], [6,
            7], [7, 8], [8, 9]])

        sl = s.slice(4, 10)
        self.assertEquals(len(sl), 6)
        self.assertEquals(sl.list_of_lists(), [[4, 5], [5, 6], [6,
            7], [7, 8], [8, 9], [9, 10]])

        sl = s.slice(4, 11)
        self.assertEquals(len(sl), 7)
        self.assertEquals(sl.list_of_lists(), [[4, 5], [5, 6], [6,
            7], [7, 8], [8, 9], [9, 10], [10, 11]])

        sl = s.slice(4, 20)
        self.assertEquals(len(sl), 16)
        self.assertEquals(sl.list_of_lists(), [[4, 5], [5, 6], [6,
            7], [7, 8], [8, 9], [9, 10], [10, 11], [11, 12], [12, 13], [13,
                14], [14, 15], [15, 16], [16, 17], [17, 18], [18, 19], [19,
                    20]])

        self.assertEquals(s.slice(4, 30).list_of_lists(), [[4, 5], [5, 6], [6,
            7], [7, 8], [8, 9], [9, 10], [10, 11], [11, 12], [12, 13], [13,
                14], [14, 15], [15, 16], [16, 17], [17, 18], [18, 19], [19,
                    20], [20, 21], [21, 22], [22, 23], [23, 24], [24, 25], [25,
                        26], [26, 27], [27, 28], [28, 29], [29, 30]])

        self.assertEquals(s.slice(11, 19).list_of_lists(), [[11, 12], [12, 13],
            [13, 14], [14, 15], [15, 16], [16, 17], [17, 18], [18, 19]])

        self.assertEquals(s.slice(14, 19).list_of_lists(), [[14, 15], [15, 16],
            [16, 17], [17, 18], [18, 19]])

        self.assertEquals(s.slice(14, 21).list_of_lists(), [[14, 15], [15, 16],
            [16, 17], [17, 18], [18, 19], [19, 20], [20, 21]])

        self.assertEquals(s.slice(14).list_of_lists(), [[0, 1], [1, 2], [2, 3],
            [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9], [9, 10], [10, 11],
            [11, 12], [12, 13], [13, 14]])

        self.assertEquals(s.slice(14, None).list_of_lists(), [[14, 15], [15,
            16], [16, 17], [17, 18], [18, 19], [19, 20], [20, 21], [21, 22],
            [22, 23], [23, 24], [24, 25], [25, 26], [26, 27], [27, 28], [28,
                29], [29, 30]])

        for amt, gap in [(1, 30), (2, 15), (3, 10), (5, 6), (6, 5), (10, 3),
                (15, 2), (30, 1)]: 

            for i in xrange(amt):
                self.assertEquals(s.get_group(i, amt).list_of_lists(), [[x, x + 1]
                    for x in xrange(i * gap, i * gap + gap)])

    def test_divide(self):
        a = DataTable('a')
        for i in xrange(200):
            a.add(i)

        portions = list(a.split_into(10))
        l = [len(x) for x in portions]
        self.assertEquals(len(l), 10)
        self.assertEquals(len(filter(lambda x: x != 20, l)), 0)

        for i in xrange(10):
            self.assertEquals(list(portions[i]), range(i * 20, (i + 1) * 20))

        for n in (10, 11, 24, 46):
            x = a.split_by(n)
            for i, value in enumerate(x):
                self.assertEquals(list(value), range(i, 200, n))

#        l = 10
#        values = list(x.split_into(l))
#        self.assertEquals(len(values), l)
#        for item in values:
#            print len(item)

    def test_group(self):
        a = DataTable('a', 'b', 'c')
        a.add(1, 2, 3)
        a.add(1, 4, 5)
        a.add(2, 6, 7)
        a.add(3, 8, 9)
        a.add(3, 10, 11)
        g = list(a.group('a'))
        self.assertEquals([gi[0] for gi in g], range(1, 4))
        self.assertEquals([list(gi) for gi in g[0][1]], [[1, 2, 3], [1, 4, 5]])
        self.assertEquals([list(gi) for gi in g[1][1]], [[2, 6, 7]])
        self.assertEquals([list(gi) for gi in g[2][1]], [[3, 8, 9], [3, 10, 11]])

        g = list(a.group('a', assume_ordered=True))
        self.assertEquals([gi[0] for gi in g], range(1, 4))
        self.assertEquals([list(gi) for gi in g[0][1]], [[1, 2, 3], [1, 4, 5]])
        self.assertEquals([list(gi) for gi in g[1][1]], [[2, 6, 7]])
        self.assertEquals([list(gi) for gi in g[2][1]], [[3, 8, 9], [3, 10, 11]])

    def test_fuse(self):
        a = DataTable('e', 'a', 'b', 'c')
        b = DataTable('a', 'b', 'd', 'f')
        a.add(-1, 0, 1, 2)
        b.add(1, 2, 3, 4)

        c = a.fuse(b)
        self.assertEquals(c.column_names, ('a', 'b'))
        self.assertEquals([list(x) for x in c], [[0, 1], [1, 2]])

        c = DataTable('a', 'b', 'g')
        c.add(5, 6, 7)

#        d = DataTable.connect(a, b, c)
#        self.assertEquals(len(d), 3)
#
#        self.assertEquals(d.list_of_lists(), [[0, 1], [1, 2], [5, 6]])

    def test_affectors(self):
        d = self.make()
        self.assertEquals(len(d.filter(lambda x: x[0] > 1)), 1)

        a, b = d.filtersplit(lambda x: x[0] > 1)
        self.assertEquals(list(list(a)[0]), [4, 5, 6])
        self.assertEquals(list(list(b)[0]), [1, 2, 3])

        e = d.modify(lambda x: -x[0])
        self.assertEquals(len(d._data), len(d))
        self.assertEquals(len(e._data), len(d))
        self.assertEquals(len(d._data), len(e))
        self.assertEquals(list(e), list(-d.columns(0)))

    def test_check_indexes(self):
        y = DataTable('a', 'b', 'c')
        self.assertEquals(y._check_indexes(0), [0])
        self.assertEquals(y._check_indexes(1), [1])
        self.assertEquals(y._check_indexes(2), [2])

        self.assertEquals(y._check_indexes('a'), [0])
        self.assertEquals(y._check_indexes('b'), [1])
        self.assertEquals(y._check_indexes('c'), [2])

        self.assertEquals(y._check_indexes(-1), [2])
#        self.assertEquals(y._check_indexes(3), [0]) # XXX

        self.assertEquals(y._check_indexes('0'), [0]) 
        self.assertEquals(y._check_indexes('1'), [1]) 
        self.assertEquals(y._check_indexes('2'), [2]) 
#        self.assertEquals(y._check_indexes('-1'), [2]) # XXX

    def test_ordinalize(self):
        y = DataTable('a', 'b', 'c')
        y.add(.0, .1, .2)
        y.add(.1, .2, .0)
        y.add(.2, .0, .1)
        checker = y.empty_copy()
        checker.add(0, 1, 2)
        checker.add(1, 2, 0)
        checker.add(2, 0, 1)
        self.assertEquals(y.ordinalize(), checker)
        self.assertEquals(y.ordinalize(1, 2).list_of_lists(), [[1, 2], [2, 0], [0, 1]])

        d = DataTable()
        d.add(1)
        d.add(2)
        d.add(0)
        self.assertEquals(d.ordinalize(), d)

    def test_delete(self):
        d = DataTable('a', 'b', 'c')
        d.add(1, 2, 3)
        d.add(4, 5, 6)
        d.add(7, 8, 9)

        self.assertEquals(d.minus('a').list_of_lists(), [[2, 3], [5, 6], [8, 9]])
        self.assertEquals(d.minus('b').list_of_lists(), [[1, 3], [4, 6], [7, 9]])
        self.assertEquals(d.minus('b', 'c'), d.columns(0))

    def x_test_normalize(self):
        y = DataTable('y')

        y.add(2)
        y.add(4)
        y.add(None)
        y.add(6)
        y.add(6)

        self.assertEquals(list(y.normalize()), [0, .5, None, 1, 1])

        x = DataTable('x', 'y')
        x.add(0, None)
        x.add(2, None)
        x.add(4, 0)
        xp = list(x.normalize())
        self.assertEquals(list(xp[0]), [0, None])
        self.assertEquals(list(xp[1]), [.5, None])
        self.assertEquals(list(xp[2]), [1, 0])

        x.add(None, 3)
        xp = list(x.normalize())
        self.assertEquals(list(xp[0]), [0, None])
        self.assertEquals(list(xp[1]), [.5, None])
        self.assertEquals(list(xp[2]), [1, 0])
        self.assertEquals(list(xp[3]), [None, 1])

#        print x.list_of_lists()
        xp = x.normalize2()
        self.assertEquals([list(x) for x in xp.columns()], [[0.0, 0.33333333333333331, 0.66666666666666663, None], [None, None, 0.0, 1.0]])

        x = DataTable('x', 'y')
        x.add('hello', 0)
        x.add('what', 2)
        xp = list(x.normalize())
        self.assertEquals(list(xp[0]), ['hello', 0])
        self.assertEquals(list(xp[1]), ['what', 1])

    def test_columns(self):
        d = DataTable('a', 'b', 'c')
        d.add(1, 2, 3)
        d.add(4, 5, 6)
        d.add(7, 8, 9)
        columns = d.columns()
        d2 = DataTable('a', 'b', 'c').extend(zip(*columns))
        self.assertEquals(d, d2)
#        print 'c', d.columns(1).list_of_lists()

    def test_operations(self):
        d = self.make()
        
        self.assertEquals(list(d.columns(0) * d.columns(1)), [2, 20])
        self.assertRaises(TypeError, lambda x: list(d.__mul__(x)), d)
#        self.assertEquals(list(d.columns(0) + d.columns(1)), [3, 9])
        self.assertEquals(list(d.columns(0) - d.columns(1)), [-1, -1])
        self.assertEquals(list(d.columns(0) / d.columns(1)), [.5, 4. / 5])

        b = DataTable('d', 'e')
        b.add(4, 5)

        e = d | b
        self.assertEquals(e.column_names, tuple('abcde'))
        self.assertEquals(list(e)[0], tuple(range(1, 6)))

        e = d | b.columns(0)

        x = DataTable('x')
        y = DataTable('y')

        x.add(1)
        x.add(None)
        x.add(2)
        x.add(None)
        x.add(3)

        y.add(2)
        y.add(4)
        y.add(None)
        y.add(6)
        y.add(6)

        m = x / y
        m.rename('xdivy')
        self.assertEquals(list(m), [0.5, None, None, None, 0.5])

        # __neg__
        self.assertEquals(list(-m), [-0.5, -0.5])
        m = x | y
        self.assertEquals([list(z) for z in -m], [[-1, -2], [-3, -6]])

        m1, m2 = m.unzip('y')
        self.assertEquals(m1.column_names, ('x',))
        self.assertEquals(m2.column_names, ('y',))
        self.assertEquals(list(m1), [1, None, 2, None, 3])
        self.assertEquals(list(m2), [2, 4, None, 6, 6])

    def test_lengths(self):
        d = StreamingTable('c', 'd').extend([(x, x + 1) for x in xrange(24)])
        self.assertEquals(len(d), 24)
        x = d.filter(lambda x: x > 3)
        self.assertRaises(TypeError, len, x)

    def test_extend(self):
        d = self.make()
        self.assertRaises(ValueError, d.extend, d)
        d.extend(d.copy())
        dl = list(d)
        self.assertEquals(len(dl), 4)
        d.extend([[1, 2, 3]])

        d = d.columns(0)
        d.extend([1, 2, 3])
        self.assertEquals(list(d), [1, 4, 1, 4, 1, 1, 2, 3])

        d = DataTable().extend(xrange(5)) + DataTable().extend(xrange(5, 10))
        self.assertEquals(list(d), range(10))

    def test_columns2(self):
        d = self.make()
        self.assertEquals(d.narrow_after(1), d.narrow(1, 2))
        self.assertEquals(d.narrow_range(0, 1), d.narrow(0))
        self.assertEquals(d.narrow_range(0, 2), d.narrow(0, 1))
        self.assertEquals(d.narrow_range(0, -1), d.narrow(0, 1))
        self.assertEquals(d.narrow_range(0, -2), d.narrow(0))
        self.assertEquals(d.narrow_range(1, -1), d.narrow(1))

    def test_clean(self):
        d = self.make()
        d.add(7, 8, None)

        self.assertEquals(list(d), [(1, 2, 3), (4, 5, 6), (7, 8, None)])
        self.assertEquals(list(d.clean()), [(1, 2, 3), (4, 5, 6)])

        d = DataTable().extend((1, None, 2, 3, None, 4))
        self.assertEquals(d.clean(), xrange(1, 5))

    def test_range(self):
        d = DataTable()
        d.add(1, 2)
        d.add(2, 3)
        d.add(3, 4)

        self.assertEquals(d.range(0, 1).list_of_lists()[0], [1, 2])
        self.assertEquals(d.range(0, 2).list_of_lists(), [[1, 2], [2, 3]])
        self.assertEquals(d.range(1, 3).list_of_lists(), [[2, 3], [3, 4]])
        self.assertEquals(d.range(1).list_of_lists(), d.range(0, 1).list_of_lists())
#        print d.range(1, 8).list_of_lists()

    def test_narrow(self):
        d = DataTable()
        d.add('hello', 0, 1)
        d.add('ok', 1, 2)
#        self.assertEquals(d.narrow_value_columns().column_names, ('b', 'c'))
#        a, b = d.narrow_value_columns().columns()
#        self.assertEquals(list(a), [0, 1])
#        self.assertEquals(list(b), [1, 2])

    def x_test_stats(self):
        d = self.make()
        self.assertEquals(d.mean(), (2.5, 3.5, 4.5))
        print d.columns(0).list_of_lists()
        self.assertEquals(d.sum(), (5, 7, 9))

        mean_fail = DataTable('a')
        self.assertEquals(mean_fail.mean(), None)

        self.assertEquals(d.columns(0).mean(), 2.5)
        self.assertEquals(d.columns('b').mean(), 3.5)
        self.assertEquals(d.columns('b').stddev(), 1.5)

        self.assertEquals(d.columns(0).skew(), 0)
        self.assertEquals(d.columns(0).kurtosis(), -2)

        self.assertEquals((d.columns(0).max(), d.columns(1).max()), (4, 5))
        self.assertEquals(d.max(), (4, 5, 6))
        self.assertEquals((d.columns(0).min(), d.columns(1).min()), (1, 2))
        self.assertEquals(d.columns(0).minmax(), (d.columns(0).min(), d.columns(0).max()))

        d = DataTable('empty')
        self.assertEquals(d.max(), None)

        a = DataTable('a', 'b')
        a.add(0, 2)
        a.add(9, 6)
        a.add(6, 2)
        a.add(2, 1)
        a.add(1, 0)
        a.add(4, 4)
        r = a.ttest_ind(0, 1)
        self.assertAlmostEquals(r[0], 0.71074231559353318)
        self.assertAlmostEquals(r[1], 0.49348706483340998)

        self.assertEquals(a.cumfreq(0).list_of_lists(), [[0.0, 0.16666666666666666], [1.0, 0.33333333333333331], [2.0, 0.5], [3.0, 0.5], [4.0, 0.66666666666666663], [5.0, 0.66666666666666663], [6.0, 0.83333333333333337], [7.0, 0.83333333333333337], [8.0, 0.83333333333333337], [9.0, 1.0]])

    def make(self):
        d = DataTable('a', 'b', 'c')
        d.add(1, 2, 3)
        d.add(4, 5, 6)
        return d

    def test_shuffle(self):
        from core.itertools2 import iteriter
        l = 500
        d = DataTable('a').extend(xrange(l))
        d = d.shuffle()
        self.assertNotEqual(list(iteriter(d.list_of_lists())), range(l))
        d = list(iteriter(d.list_of_lists()))
        d.sort()
        self.assertEquals(d, range(l))

    def test_a(self):
        self.assertRaises(ValueError, DataTable, 'a', 'a')
        d = self.make()

        self.assertEquals(reduce(list.__add__, [list(x) for x in d], []), range(1, 7))

        a = map(list, d.narrow(1, 2).columns(0, 1))

        self.assertEquals(map(list, d.narrow(1, 2).columns(0, 1)), [[2, 5], [3, 6]])
        self.assertEquals(d.narrow(1, 2).column_names, ('b', 'c'))
        self.assertEquals(map(list, d.narrow(0, 1).columns(0, 1)), [[1, 4], [2, 5]])
        self.assertEquals(d.narrow(0, 1).column_names, ('a', 'b'))
        self.assertEquals(map(list, d.narrow(0, 2).columns(0, 1)), [[1, 4], [3, 6]])
        self.assertEquals(d.narrow(0, 2).column_names, ('a', 'c'))

        self.assertEquals(list(d.columns(0)), [1, 4])
        self.assertEquals(list(d.columns(1)), [2, 5])
        self.assertEquals(list(d.columns(2)), [3, 6])
        self.assertEquals(map(list, d.columns(0, 1)), [[1, 4], [2, 5]])
        self.assertEquals(map(list, d.columns(0, 2)), [[1, 4], [3, 6]])
        self.assertEquals(map(list, d.columns(1, 2)), [[2, 5], [3, 6]])
        self.assertEquals(list(d.narrow(0, 1, 2)), [(1, 2, 3), (4, 5, 6)])
        self.assertEquals(map(list, d.columns(2, 1)), [[3, 6], [2, 5]])
        self.assertEquals(map(list, d.columns('a', 'b')), map(list, d.columns(0, 1)))
        self.assertEquals(map(list, d.columns('a', 2)), map(list, d.columns(0, 'c')))
        self.assertEquals(map(list, d.columns('c', 0, 'a')), map(list, d.columns(2, 'a', 0)))

        d.sort_by(1, comparator=lambda x, y: cmp(1. / x, 1. / y))
        self.assertEquals(list(d), [(4, 5, 6), (1, 2, 3)])
        d.sort_by('b', comparator=lambda x, y: cmp(1. / x, 1. / y))
        self.assertEquals(list(d), [(4, 5, 6), (1, 2, 3)])

        self.assertRaises(ValueError, d.sort_by, 'z', comparator=lambda x, y: cmp(1. / x, 1. / y))
        self.assertEquals([list(x) for x in d.columns()], [[4, 1], [5, 2], [6, 3]])

        # raise error on spaces in column names
        self.assertRaises(ValueError, DataTable, 'a', 'b c')

    def test_sort(self):
        d = DataTable('a')
        for i in xrange(3, 0, -1):
            d.add(i)

        self.assertEquals(list(d.sort_by('a')), range(1, 4))

    def test_index(self):
        d = self.make()
        di = d.index('a')
        dx = list(d)
        self.assertEquals(di[1], dx[0])
        self.assertEquals(di[4], dx[1])

        di = d.index('a', 'c')
        self.assertEquals(len(di), 2)
        self.assertEquals(di[1], 3)
        self.assertEquals(di[4], 6)

        a = DataTable('a', 'b', 'c', 'd')
        a.add(*range(4))
        a.add(*range(4, 8))
        a.add(*range(8, 12))

        d = a.nested_index('a')
        self.assertEquals(list(d[0]), [0, 1, 2, 3])
        self.assertEquals(list(d[4]), [4, 5, 6, 7])
        self.assertEquals(list(d[8]), [8, 9, 10, 11])

        d = a.nested_index('a', 'b')
        self.assertEquals(len(d.keys()), 3)
        self.assertEquals(set(d.keys()), set(range(0, 9, 4)))
        for i in xrange(0, 9, 4):
            self.assertEquals(len(d[i]), 1)

        self.assertEquals(d[0].keys(), [1])
        self.assertEquals(d[4].keys(), [5])
        self.assertEquals(d[8].keys(), [9])

        self.assertEquals(list(d[0][1]), range(4))
        self.assertEquals(list(d[4][5]), range(4, 8))
        self.assertEquals(list(d[8][9]), range(8, 12))

        d = a.nested_index('a', 'b', 'c')

        self.assertEquals(len(d.keys()), 3)
        self.assertEquals(set(d.keys()), set(range(0, 9, 4)))
        for i in xrange(0, 9, 4):
            self.assertEquals(len(d[i]), 1)

        self.assertEquals(d[0].keys(), [1])
        self.assertEquals(d[4].keys(), [5])
        self.assertEquals(d[8].keys(), [9])

        self.assertEquals(list(d[0][1][2]), range(4))
        self.assertEquals(list(d[4][5][6]), range(4, 8))
        self.assertEquals(list(d[8][9][10]), range(8, 12))

        d = DataTable('a', 'b', 'c')
        d.add(0, 0, 0)
        d.add(0, 0, 1)
        d.add(0, 1, 0)
        d.add(0, 1, 1)

        x = d.nested_index('a', 'b')
        self.assertEquals(x.keys(), [0])
        self.assertEquals(x[0].keys(), [0, 1])

        x = d.nested_index('a', 'c')
        self.assertEquals(list(x[0][0]), [0, 1, 0])
        self.assertEquals(list(x[0][1]), [0, 1, 1])

    def test_combine(self):
        d1 = self.make()

        d2 = DataTable('a', 'd', 'e')
        d2.add(1, 4, 5)
        d2.add(5, 6, 7)

        d3 = d2.copy()
        d3.list_of_lists()
        d3.add(1, 0)

        self.assertRaises(TypeError, d3.list_of_lists)
        d3 = d2.copy()
        d3.list_of_lists()
        d3.add(1, 3, 4, 5)
        self.assertRaises(TypeError, d3.list_of_lists)

        j = DataTable.outer_join(d1, d2)
        self.assertEquals(j.column_names, tuple('abcde'))
        self.assertEquals(list(list(j)[0]), range(1, 6))
        self.assertEquals(list(list(j)[1]), [4, 5, 6, None, None])
        self.assertEquals(list(list(j)[2]), [5, None, None, 6, 7])

#        print d1.list_of_lists()
#        print d2.list_of_lists()

        jt = DataTable.join(d1, d2)
        self.assertEquals(list(list(jt)[0]), range(1, 6))

        d3 = DataTable('f', 'g', 'a')
        d3.add(6, 7, 1)
        d3.add(10, 11, 9)
        j = DataTable.outer_join(j, d3)
        self.assertEquals(j.column_names, tuple('abcdefg'))
        jl = list(j)
        self.assertEquals(list(jl[0]), range(1, 8))
        self.assertEquals(list(jl[1]), range(4, 7) + [None, None, None, None])
        self.assertEquals(list(jl[2]), [5, None, None, 6, 7, None, None])
        self.assertEquals(list(jl[3]), [9, None, None, None, None, 10, 11])
        self.assertEquals(list(DataTable.outer_join(d1, d2, d3)), list(j))

        d1 = DataTable('a', 'b')
        d2 = DataTable('a', 'c')

        d1.add(1, 0)
        d1.add(None, 0)
        d2.add(1, 0)

        d = DataTable('a', 'b')
        d.add(0, 1)
        d.add(2, 3)

        d2 = DataTable('a', 'c')
        d2.add(0, 2)

        self.assertEquals(DataTable.join(d, d2).list_of_lists(), [[0, 1, 2]])

        d2.add(3, 1)
        self.assertEquals(DataTable.join(d, d2).list_of_lists(), [[0, 1, 2]])
        self.assertEquals(DataTable.outer_join(d, d2).list_of_lists(), [[0, 1, 2], [2, 3, None], [3, None, 1]])
        self.assertEquals(DataTable.left_outer_join(d, d2).list_of_lists(), [[0, 1, 2], [2, 3, None]])

        #print list(DataTable.outer_join(d1, d2))

    def test_empty_join(self):
        a = DataTable('a', 'b')
        a.add(1, 2)
        b = DataTable('a', 'c')
        b.extend(xrange(0))

        x = DataTable.join(a, b)
        self.assertEquals(x.list_of_lists(), [])

    def test_tuple(self):
        dt = list(self.make())
        self.assertEquals(dt[0].a, 1)
        self.assertEquals(dt[0].b, 2)
        self.assertEquals(dt[0].c, 3)
        self.assertEquals(dt[1].a, 4)
        self.assertEquals(dt[1].b, 5)
        self.assertEquals(dt[1].c, 6)

    def test_streaming(self):
        a = ((x,) for x in xrange(10))
        d = DataTable()
        d.extend(a)
        self.assertEquals(d.width(), 1)

        a = ((x, x + 1) for x in xrange(10))
        d = DataTable()
        d.extend(a)
        self.assertEquals(d.width(), 2)

        a = ((x, x + 1) for x in xrange(10))
        d = NonRetainingStream()
        d.extend(a)
        self.assertEquals(d.width(), 2)
        self.assertEquals(list(iter(d)), [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (8, 9), (9, 10)])


if __name__ == '__main__':
    unittest.main()
